package rabbit.flt.common.context;

import rabbit.flt.common.trace.MethodStackInfo;
import rabbit.flt.common.trace.TraceData;
import rabbit.flt.common.utils.StringUtils;
import rabbit.flt.common.utils.UUIDUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * trace 元数据
 */
public class TraceMetaData {

    /**
     * 链路id
     */
    private String traceId;

    /**
     * 开启trace的对象
     */
    private Object traceOpenObject;


    /**
     * 节点 计数器
     */
    private AtomicLong nodeCounter;

    /**
     * 方法堆栈信息
     */
    private Map<Method, MethodStackInfo> methodStacks;

    /**
     * span id 计数器
     */
    private Map<String, AtomicLong> spanIdCounter;

    /**
     * spring web / web flux支持类，用来传递trace data给controller插件
     */
    private TraceData webTraceData;

    /**
     * 发生错误的 method
     */
    private Method errorMethod;

    /**
     * 一旦被锁定就不能再修改了
     */
    private boolean webTraceDataLocked = false;

    public TraceMetaData() {
    }

    public TraceMetaData(Object target) {
        this();
        methodStacks = new ConcurrentHashMap<>();
        spanIdCounter = new ConcurrentHashMap<>();
        nodeCounter = new AtomicLong(1l);
        this.traceOpenObject = target;
        setTraceId(StringUtils.toString(UUIDUtils.uuid()));
    }

    /**
     * 获取span id 计数器
     *
     * @param spanId
     * @return
     */
    public AtomicLong getSpanIdChildCounter(String spanId) {
        return spanIdCounter.computeIfAbsent(spanId, k -> new AtomicLong(0));
    }

    /**
     * 增加节点个数
     */
    public long incrementNode() {
        return nodeCounter.incrementAndGet();
    }

    /**
     * 判断是不是发生错误的方法
     *
     * @param method
     * @return
     */
    public boolean isErrorMethod(Method method) {
        return null != errorMethod && errorMethod == method;
    }

    public String getTraceId() {
        return traceId;
    }

    public void setTraceId(String traceId) {
        this.traceId = traceId;
    }

    public Object getTraceOpenObject() {
        return traceOpenObject;
    }

    public Map<Method, MethodStackInfo> getMethodStacks() {
        return methodStacks;
    }

    public TraceData getWebTraceData() {
        return webTraceData;
    }

    public void setWebTraceData(TraceData webTraceData) {
        this.webTraceData = webTraceData;
    }

    /**
     * 尝试保存异常方法对象
     *
     * @param errorMethod
     */
    public void trySaveErrorMethod(Method errorMethod) {
        if (null == this.errorMethod) {
            this.errorMethod = errorMethod;
        }
    }

    public AtomicLong getNodeCounter() {
        return nodeCounter;
    }

    /**
     * 判断是否被锁定
     *
     * @return
     */
    public boolean isWebTraceDataLocked() {
        return webTraceDataLocked;
    }

    public void setWebTraceDataLocked(boolean webTraceDataLocked) {
        this.webTraceDataLocked = webTraceDataLocked;
    }
}
